package com.njcb.ams.support.trade;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.njcb.ams.factory.domain.AppContext;
import com.njcb.ams.support.exception.AppException;
import com.njcb.ams.support.exception.ExceptionUtil;
import com.njcb.ams.support.trade.annotation.TradeParam;
import com.njcb.ams.support.trade.bean.TradeServiceBean;
import com.njcb.ams.support.trade.filter.TradeLayerFilter;
import com.njcb.ams.util.AmsAssert;
import com.njcb.ams.util.AmsBeanUtils;
import com.njcb.ams.util.AmsUtils;

/**
 * 交易服务类、提供交易相关操作，如获取交易代码、反射调用等。
 * @author LOONG
 */
@Service
public class TradeService {
	private static final Logger logger = LoggerFactory.getLogger(TradeService.class);
	/**
	 * 交易过滤器
	 */
	private static List<TradeLayerFilter> filterList = new ArrayList<>();
	/**
	 * 交易过滤器-临时
	 */
	private static TradeLayerFilter[] filters;;
	/**
	 * 交易集合
	 */
	private static Map<String, TradeServiceBean> tradeMap = new HashMap<String, TradeServiceBean>();
	/**
	 * 交易挡板服务集合
	 */
	private static Map<String, TradeServiceBean> tradeHandleMap = new HashMap<String, TradeServiceBean>();
	
	public static TradeService getInstance() {
		logger.debug("Instance TradeService");
		return AppContext.getBean(TradeService.class);
	}
	
	/**
	 * 
	 * 方法功能描述：根据交易代码获取交易服务Bean
	 * 
	 * @param tradeCode
	 * @return
	 */
	public static String getTradeName(String tradeCode) {
		return tradeMap.get(tradeCode).getTradeName();
	}
	
	/**
	 * 
	 * 方法功能描述：根据交易代码获取交易服务描述Bean
	 * 
	 * @param tradeCode
	 * @return
	 */
	public static TradeServiceBean getTradeServiceBean(String tradeCode) {
		TradeServiceBean tradeBean = tradeMap.get(tradeCode);
		AmsAssert.notNull(tradeBean, "交易代码[" + tradeCode + "]不存在");
		return tradeBean;
	}
	
	/**
	 * 
	 * 方法功能描述：根据交易代码获取交易服务Bean
	 * 
	 * @param tradeCode
	 * @return
	 */
	public static Object getTradeService(String tradeCode) {
		TradeServiceBean tradeBean = tradeMap.get(tradeCode);
		AmsAssert.notNull(tradeBean, "交易代码[" + tradeCode + "]不存在");
		return AppContext.getBean(tradeBean.getBeanName());
	}
	
	/**
	 * 
	 * 方法功能描述：根据交易代码获取交易函数
	 * 
	 * @param tradeCode
	 * @return
	 */
	public static Method getTradeServiceMethod(String tradeCode) {
		TradeServiceBean tradeBean = tradeMap.get(tradeCode);
		AmsAssert.notNull(tradeBean, "交易代码[" + tradeCode + "]不存在");
		return tradeBean.getMethod();
	}
	
	/**
	 * 方法功能描述：根据交易代码获取交易输入字段
	 * @param tradeCode
	 */
	public static List<Field> getTradeInputField(String tradeCode) {
		List<Field> rtList = new ArrayList<>();
		Method method = getTradeServiceMethod(tradeCode);
		Integer parCount = method.getParameterCount();
		for (int i = 0; i < parCount; i++) {
			Class<?> paramClass = method.getParameters()[i].getType();
			Field[] fields = paramClass.getDeclaredFields();
			for (Field field : fields) {
				rtList.add(field);
			}
		}
		return rtList;
	}
	
	
	/**
	 * 方法功能描述：根据交易代码获取交易输出字段
	 * @param tradeCode
	 */
	public static List<Field> getTradeOutputField(String tradeCode) {
		List<Field> rtList = new ArrayList<>();
		Method method = getTradeServiceMethod(tradeCode);
		Class<?> paramClass = method.getReturnType();
		Field[] fields = paramClass.getDeclaredFields();
		for (Field field : fields) {
			rtList.add(field);
		}
		return rtList;
	}
	
	
	/**
	 * 根据交易码调用交易
	 * @param tradeCode 交易码
	 * @param inputParam 输入参数
	 * @return 输出值
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public static Map<String, Object> executeTrade(String tradeCode, Map<String, Object> inputParam) {
		Object returnObject = execute(tradeCode, inputParam);
		if(null == returnObject){
			return new HashMap<String, Object>();
		}
		if(Map.class.isAssignableFrom(returnObject.getClass())){
			return (Map<String, Object>) returnObject;
		}
		Map<String, Object> returnMap = AmsBeanUtils.describe(returnObject);
		return returnMap;
	}
	
	/**
	 * 根据交易码调用交易
	 * @param tradeCode 交易码
	 * @param inputBean 输入Bean
	 * @return 输出值
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public Map<String, Object> executeTrade(String tradeCode, Object inputBean) {
		Object returnObject = execute(tradeCode, inputBean);
		if(null == returnObject){
			return new HashMap<String, Object>();
		}
		if(Map.class.isAssignableFrom(returnObject.getClass())){
			return (Map<String, Object>) returnObject;
		}
		Map<String, Object> returnMap = AmsBeanUtils.describe(returnObject);
		return returnMap;
	}
	
	/**
	 * 根据交易码调用交易
	 * @param tradeCode 交易码
	 * @param inputBean 输入Bean
	 * @return 输出Bean
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public <T> T executeTrade(String tradeCode, Object inputBean,Class<T> outputBeanClass) {
		Object returnObject = execute(tradeCode, inputBean);
		try {
			return (T) populate(outputBeanClass, returnObject);
		} catch (Exception e) {
			ExceptionUtil.printStackTrace(e);
			ExceptionUtil.throwAppException("交易[" + tradeCode + "]调用结果异常:" + AmsUtils.isNull(e.getMessage()) != null?e.getClass().getSimpleName():e.getMessage());
		}
		return null;
	}
	
	
	/**
	 * 根据交易码调用交易
	 * @param tradeCode 交易码
	 * @param inputObject 输入参数
	 * @return 输出值
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	private static Object execute(String tradeCode, Object inputObject) {
		TradeServiceBean tradeBean = tradeMap.get(tradeCode);
		AmsAssert.notNull(tradeBean, "交易代码[" + tradeCode + "]不存在");
		Object tradeService = AppContext.getBean(tradeBean.getBeanName());
		Method tradeMethod = tradeBean.getMethod();
		Object returnObject = null;
		int parameterCount = tradeMethod.getParameterCount();
		Class<?>[] parameterTypes = tradeMethod.getParameterTypes();
		Annotation[][] parameterAnnotations = null;
		try {
			Object[] paramArr = new Object[parameterCount];
			for (int i = 0; i < parameterCount; i++) {
				Class<?> paramClass = parameterTypes[i];
				Object param = null;
				if(paramClass.isAssignableFrom(String.class) && inputObject instanceof Map){
					if(null == parameterAnnotations){
						parameterAnnotations = tradeMethod.getParameterAnnotations();
					}
					if(null == parameterAnnotations){
						continue;
					}
					for (Annotation annotation : parameterAnnotations[i]) {
						if(annotation instanceof TradeParam){
							param = ((Map<String, Object>)inputObject).get(((TradeParam) annotation).value());
						}
					}
				}else{
					param = populate(paramClass, inputObject);
				}
				paramArr[i] = param;
			}
			returnObject = tradeMethod.invoke(tradeService, paramArr);
			logger.info("交易[" + tradeCode + "]返回类型为[" + returnObject.getClass().getSimpleName() + "]");
			return returnObject;
		} catch (Throwable e) {
			if (null != e.getCause()) {
				e = e.getCause();
			}
			if (null != e.getCause()) {
				e = e.getCause();
			}
			ExceptionUtil.printStackTrace(e);
			if(e instanceof AppException){
				throw (AppException)e;
			}
			if(e instanceof InvocationTargetException){
				e = ((InvocationTargetException)e).getTargetException();
			}
			if(e instanceof UndeclaredThrowableException){
				e = ((UndeclaredThrowableException)e).getUndeclaredThrowable();
			}
			ExceptionUtil.throwAppException("交易[" + tradeCode + "]调用异常:" + e.getClass().getSimpleName() + e.getMessage());
		}
		return null;
	}
	
	
	/**
	 * 方法功能描述：属性移植
	 * 
	 * @param beanClass
	 * @param properties
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	private static Object populate(Class<?> beanClass, Object properties) throws Exception {
		Object param = null;
		if(Map.class.isAssignableFrom(beanClass) ){
			param = new HashMap<String, Object>();
		}else{
			param = beanClass.newInstance();
		}
		
		if(properties instanceof Map && param instanceof Map ){
			((Map<String, Object>)param).putAll((Map<String, Object>)properties);
		}else if(properties instanceof Map){
			AmsBeanUtils.populate(param, (Map<String, Object>)properties);
		}else{
			AmsBeanUtils.copyProperties(param, properties);
		}
		
		return param;
	}
	
	/**
	 * 添加交易
	 * @param tradeBean
	 */
	public static void addTrade(TradeServiceBean tradeBean) {
		//检查挡板与正常服务函数描述一致
    	if(tradeHandleMap.containsKey(tradeBean.getTradeCode())){
    		TradeServiceBean hdlTradeBean = tradeHandleMap.get(tradeBean.getTradeCode());
    		Method tdeMethod = tradeBean.getMethod();
    		Method hdlMethod = hdlTradeBean.getMethod();
    		//检查返回类型
    		if(tdeMethod.getReturnType().getClass() != hdlMethod.getReturnType().getClass()){
    			ExceptionUtil.throwAppException("交易" + tradeBean.getTradeCode() + "]的服务函数定义与挡板函数定义返回类型不一致");
    		}
    		//检查参数个数
    		if(tdeMethod.getParameterCount() != hdlMethod.getParameterCount()){
    			ExceptionUtil.throwAppException("交易" + tradeBean.getTradeCode() + "]的服务函数定义与挡板函数定义参数个数不一致");
    		}
    		Parameter[] tdeParam = tdeMethod.getParameters();
    		Parameter[] hdlParam = hdlMethod.getParameters();
    		//检查参数类型
    		for (int i = 0; i < tdeMethod.getParameterCount(); i++) {
    			if(tdeParam[i].getClass() != hdlParam[i].getClass()){
    				ExceptionUtil.throwAppException("交易" + tradeBean.getTradeCode() + "]的服务函数定义与挡板函数定义参数类型不一致");
    			}
    		}
    	}
		tradeMap.put(tradeBean.getTradeCode(), tradeBean);
	}
	
	
	/**
	 * 挡板添加交易
	 * @param tradeBean
	 */
	public static void addTradeHandle(TradeServiceBean tradeBean) {
		//检查挡板与正常服务函数描述一致
		if(tradeMap.containsKey(tradeBean.getTradeCode())){
			TradeServiceBean tdeTradeBean = tradeMap.get(tradeBean.getTradeCode());
			Method tdeMethod = tdeTradeBean.getMethod();
			Method hdlMethod = tradeBean.getMethod();
			//检查返回类型
			if(tdeMethod.getReturnType().getClass() != hdlMethod.getReturnType().getClass()){
				ExceptionUtil.throwAppException("交易" + tradeBean.getTradeCode() + "]的服务函数定义与挡板函数定义返回类型不一致");
			}
			//检查参数个数
			if(tdeMethod.getParameterCount() != hdlMethod.getParameterCount()){
				ExceptionUtil.throwAppException("交易" + tradeBean.getTradeCode() + "]的服务函数定义与挡板函数定义参数个数不一致");
			}
			Parameter[] tdeParam = tdeMethod.getParameters();
			Parameter[] hdlParam = hdlMethod.getParameters();
			//检查参数类型
			for (int i = 0; i < tdeMethod.getParameterCount(); i++) {
				if(tdeParam[i].getClass() != hdlParam[i].getClass()){
					ExceptionUtil.throwAppException("交易" + tradeBean.getTradeCode() + "]的服务函数定义与挡板函数定义参数类型不一致");
				}
			}
		}
		tradeHandleMap.put(tradeBean.getTradeCode(), tradeBean);
	}

	/**
	 * 挡板添加交易
	 * @param tradeCode
	 * @return
	 */
	public static TradeServiceBean getTradeHandle(String tradeCode) {
		return tradeHandleMap.get(tradeCode);
	}
	
	/**
	 * 方法功能描述：交易过滤器
	 * @param tradeFilter
	 */
	public static void addTradeFilter(TradeLayerFilter tradeFilter) {
		filterList.add(tradeFilter);
	}
	
	/**
	 * 方法功能描述：过去交易过滤器
	 * @return
	 */
	static TradeLayerFilter[] getFilters() {
		if(null != filters){
			return filters;
		}
		TradeLayerFilter[] filters = new TradeLayerFilter[filterList.size()];
		filters = filterList.toArray(filters);
		return filters;
	}
}
